﻿using System;
using UnityEngine;

namespace Zzzxl.Util {
    [Serializable]
    public struct Vector3i {
        public static readonly Vector3i zero = new Vector3i (0, 0, 0);
        public static readonly Vector3i one = new Vector3i (1, 1, 1);
        public static readonly Vector3i forward = new Vector3i (0, 0, 1);
        public static readonly Vector3i back = new Vector3i (0, 0, -1);
        public static readonly Vector3i up = new Vector3i (0, 1, 0);
        public static readonly Vector3i bottom = new Vector3i (0, -1, 0);
        public static readonly Vector3i left = new Vector3i (1, 0, 0);
        public static readonly Vector3i right = new Vector3i (-1, 0, 0);

        public static readonly Vector3i[] directions = new Vector3i[] {
            forward,
            back,
            up,
            bottom,
            left,
            right
        };

        public int x, y, z;

        //Construtors
        public Vector3i (int x, int y) {
            this.x = x;
            this.y = y;
            this.z = 0;
        }

        public Vector3i (int x, int y, int z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public Vector3i (Vector3 position) {
            this.x = Mathf.FloorToInt (position.x);
            this.y = Mathf.FloorToInt (position.y);
            this.z = Mathf.FloorToInt (position.z);
        }

        public static Vector3i Multiply (Vector3i lhs, Vector3i rhs) {
            return new Vector3i (lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z);
        }

        public static Vector3i Divide (Vector3i lhs, Vector3i rhs) {
            return new Vector3i (lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z);
        }

        public static Vector3i Max (Vector3i lhs, Vector3i rhs) {
            return Process (lhs, rhs, Mathf.Max);
        }

        public static Vector3i Min (Vector3i lhs, Vector3i rhs) {
            return Process (lhs, rhs, Mathf.Min);
        }

        public static Vector3i Abs (Vector3i v) {
            return Process (v, Mathf.Abs);
        }

        public static Vector3i Floor (Vector3 v) {
            return ProcessTo3i (v, Mathf.FloorToInt);
        }

        public static Vector3i Ceil (Vector3 v) {
            return ProcessTo3i (v, Mathf.CeilToInt);
        }

        public static Vector3i Round (Vector3 v) {
            return ProcessTo3i (v, Mathf.RoundToInt);
        }

        public static Vector3i operator + (Vector3i lhs, Vector3i rhs) {
            return new Vector3i (lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
        }

        public static Vector3i operator - (Vector3i lhs, Vector3i rhs) {
            return new Vector3i (lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);
        }

        public static Vector3i operator * (Vector3i lhs, int factor) {
            return new Vector3i (lhs.x * factor, lhs.y * factor, lhs.z * factor);
        }

        public static Vector3i operator / (Vector3i lhs, int factor) {
            return new Vector3i (lhs.x / factor, lhs.y / factor, lhs.z / factor);
        }

        public static Vector3i operator * (Vector3i lhs, Vector3i rhs) {
            return Multiply (lhs, rhs);
        }

        public static Vector3i operator / (Vector3i lhs, Vector3i rhs) {
            return Divide (lhs, rhs);
        }

        public static bool operator == (Vector3i lhs, Vector3i rhs) {
            return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z);
        }

        public static bool operator != (Vector3i lhs, Vector3i rhs) {
            return (lhs.x != rhs.x) || (lhs.y != rhs.y) || (lhs.z != rhs.z);
        }

        //Override Equals and GetHashCode
        public override bool Equals (object obj) {
            if (obj is Vector3i == false) return false;
            Vector3i v3i = (Vector3i) obj;
            return this.x == v3i.x && this.y == v3i.y && this.z == v3i.z;
        }

        public override int GetHashCode () {
            return this.x.GetHashCode () ^ this.y.GetHashCode () << 2 ^ this.z.GetHashCode () >> 2;
        }

        public override string ToString () {
            return string.Format ("Vertor3i(x: {0}, y:{1}, z: {2}", x, y, z);
        }

        public static implicit operator Vector3 (Vector3i v3i) {
            return new Vector3 (v3i.x, v3i.y, v3i.z);
        }

        private static Vector3i Process (Vector3i v, Func<int, int> func) {
            v.x = func (v.x);
            v.y = func (v.y);
            v.z = func (v.z);
            return v;
        }

        private static Vector3i Process (Vector3i lhs, Vector3i rhs, Func<int, int, int> func) {
            lhs.x = func (lhs.x, rhs.x);
            lhs.x = func (lhs.x, rhs.x);
            lhs.x = func (lhs.x, rhs.x);
            return lhs;
        }

        private static Vector3i ProcessTo3i (Vector3 v, Func<float, int> func) {
            Vector3i v1;
            v1.x = func (v.x);
            v1.y = func (v.y);
            v1.z = func (v.z);
            return v1;
        }
    }
}